/**
 * LY.com Inc.
 * Copyright (c) 2004-2018 All Rights Reserved.
 */
package com.gitee.kamismile.igniteEx.ignite;

import com.gitee.kamismile.igniteEx.common.IginteConfigProperties;
import com.gitee.kamismile.stone.commmon.errorEnum.ErrorEnum;
import com.gitee.kamismile.stone.commmon.exception.BusinessException;
import com.gitee.kamismile.stone.commmon.util.ValueUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ignite.Ignite;
import org.apache.ignite.cluster.ClusterNode;

import org.slf4j.Logger;
import org.jetbrains.annotations.Nullable;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * @author dong.li
 * @version $Id: ServiceHandle, v 0.1 2018/10/24 13:39 dong.li Exp $
 */
@Component
public class ServiceHandle {

    final IginteConfigProperties iginteConfigProperties;

    final Ignite ignite;

    String deafultVersion = "0.0.0";

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    public ServiceHandle(IginteConfigProperties iginteConfigProperties, Ignite ignite) {
        this.iginteConfigProperties = iginteConfigProperties;
        this.ignite = ignite;
    }

    public <T> T getBusinessService(String serverName, String serviceName, Class<T> svcItf) {
//        IgniteExClusterGroup getClusterGroup = new IgniteExClusterGroup(serverName).invoke();
//        org.apache.ignite.cluster.ClusterGroup clusterGroup = getClusterGroup.getClusterGroup();
//        IginteConfigProperties.ServerProvider serverInfo = getClusterGroup.getServerInfo();
//        String version = getClusterGroup.getVersion();
//
//        return ignite.services(clusterGroup)
//                .serviceProxy(serviceName + "_" + serverInfo.getServerName() + "_" + version, svcItf, false);

        return getBusinessServiceByNodeId(serverName, serviceName, svcItf, "");
    }

    public <T> T getBusinessServiceByNodeId(String serverName, String serviceName, Class<T> svcItf, String nodeId) {
        IgniteExClusterGroup getClusterGroup = new IgniteExClusterGroup(serverName).invoke();
        org.apache.ignite.cluster.ClusterGroup clusterGroup = getClusterGroup.getClusterGroup();
        IginteConfigProperties.ServerProvider serverInfo = getClusterGroup.getServerInfo();
        String version = getClusterGroup.getVersion();
        if (StringUtils.isNotBlank(nodeId)) {
            List<ClusterNode> nodes = clusterGroup.nodes().stream().filter(v -> v.consistentId().toString().equals(nodeId)).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(nodes)) {
                logger.error("没有配置调用的服务");
                throw new BusinessException(ErrorEnum.NOSERVICE.getCode(), ErrorEnum.NOSERVICE.getMessage());
            }
            clusterGroup = clusterGroup.forNodeId(nodes.get(0).id());
        }
        return ignite.services(clusterGroup)
                .serviceProxy(serviceName + "_" + serverInfo.getServerName() + "_" + version, svcItf, false);
    }

    public IgniteExClusterGroup getClusterGroupByServerName(String serverName) {
        IgniteExClusterGroup getClusterGroup = new IgniteExClusterGroup(serverName).invoke();
        return getClusterGroup;
    }

    public class IgniteExClusterGroup {
        private String serverName;
        private org.apache.ignite.cluster.ClusterGroup clusterGroup;
        private IginteConfigProperties.ServerProvider serverInfo;
        private String version;

        public IgniteExClusterGroup(String serverName) {
            this.serverName = serverName;
        }

        public org.apache.ignite.cluster.ClusterGroup getClusterGroup() {
            return clusterGroup;
        }

        public IginteConfigProperties.ServerProvider getServerInfo() {
            return serverInfo;
        }

        public String getVersion() {
            return version;
        }

        public IgniteExClusterGroup invoke() {
            clusterGroup = ignite.cluster().forAttribute("appName", serverName);
            Map<String, IginteConfigProperties.ServerProvider> serverList = iginteConfigProperties.getServers();
            if (CollectionUtils.isEmpty(serverList)) {
                logger.error("没有配置调用的服务");
                throw new BusinessException(ErrorEnum.NOSERVICE.getCode(), ErrorEnum.NOSERVICE.getMessage());
            }
            List<String> list = serverList.keySet().stream().filter(v -> v.equals(serverName)).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(list)) {
                logger.error("没有配置调用的服务");
                throw new BusinessException(ErrorEnum.NOSERVICE.getCode(), ErrorEnum.NOSERVICE.getMessage());
            }

            serverInfo = serverList.get(list.get(0));
            List<ClusterNode> nodes = getCollect(clusterGroup);
            if (CollectionUtils.isEmpty(nodes)) {
                logger.error("没有配置调用的服务");
                throw new BusinessException(ErrorEnum.NOSERVICE.getCode(), ErrorEnum.NOSERVICE.getMessage());
            }

            version = getVersion(clusterGroup, serverInfo, nodes);
            return this;
        }

        private List<ClusterNode> getCollect(org.apache.ignite.cluster.ClusterGroup clusterGroup) {
            return clusterGroup.nodes().stream().sorted((o1, o2) -> {

                String[] versions1 = ValueUtils.isStringNull(o1.attribute("appVersion")).split("\\.");
                String[] versions2 = ValueUtils.isStringNull(o1.attribute("appVersion")).split("\\.");

                if (ValueUtils.isLongNull(versions2[0]).compareTo(ValueUtils.isLongNull(versions1[0])) != 0) {
                    return ValueUtils.isLongNull(versions2[0]).compareTo(ValueUtils.isLongNull(versions1[0]));
                } else if (ValueUtils.isLongNull(versions2[1]).compareTo(ValueUtils.isLongNull(versions1[1])) != 0) {
                    return ValueUtils.isLongNull(versions2[1]).compareTo(ValueUtils.isLongNull(versions1[1]));
                } else if (ValueUtils.isLongNull(versions2[2]).compareTo(ValueUtils.isLongNull(versions1[2])) != 0) {
                    return ValueUtils.isLongNull(versions2[2]).compareTo(ValueUtils.isLongNull(versions1[2]));
                }

                return 0;
            }).collect(Collectors.toList());
        }

        @Nullable
        private String getVersion(org.apache.ignite.cluster.ClusterGroup clusterGroup, IginteConfigProperties.ServerProvider serverInfo, List<ClusterNode> nodes) {
            String version = deafultVersion;
            if (ValueUtils.isNull(serverInfo.getServerVersion())) {
                version = ValueUtils.isStringNull(nodes.get(0).attribute("appVersion"));
                clusterGroup.forAttribute("appVersion", version);
            } else {
                version = ValueUtils.isStringNull(serverInfo.getServerVersion(), deafultVersion);
                String[] versions = version.split("\\.");

                Supplier<List<ClusterNode>> mainVersion = () -> nodes.stream().filter(v -> {
                    String[] nodeVersions = ValueUtils.isStringNull(v.attribute("appVersion")).split("\\.");
                    return versions[0].equals(nodeVersions[0]);
                }).collect(Collectors.toList());

                if (CollectionUtils.isEmpty(mainVersion.get())) {
                    throw new BusinessException(426, "Upgrade Required");
                }

                String finalVersion = version;
                List<ClusterNode> versionInfo = mainVersion.get().stream().filter(v -> {
                    return ValueUtils.isStringNull(v.attribute("appVersion")).equals(finalVersion);
                }).collect(Collectors.toList());

                if (!CollectionUtils.isEmpty(versionInfo)) {
                    version = serverInfo.getServerVersion();
                    clusterGroup.forAttribute("appVersion", version);
                } else {
                    version = ValueUtils.isStringNull(mainVersion.get().get(0).attribute("appVersion"));
                    clusterGroup.forAttribute("appVersion", version);
                }

            }
            return version;
        }
    }
}
